#pragma once
#include <vector>
#include <unordered_map>
#include <fstream>
#include <string>
#include "Util.hpp"
#include "mutex.hpp"
#include <boost/algorithm/string.hpp>

typedef struct Doc{
    std::string _title;
    std::string _body;
    std::string _url;
    uint64_t _id;
    Doc(const std::string& title,const std::string& body,const std::string& url):
    _title(title),_body(body),_url(url){}
    Doc(){}
}Doc;

typedef struct InvertedDoc{
    uint64_t _id;
    std::string _word;
    int _weight;
    InvertedDoc(uint64_t id,const std::string &word,int weight):
    _id(id),_word(word),_weight(weight){}
}InvertedDoc;

class Index
{
    private:
        Index(){}
        Index(const Index&)=delete;
        Index& operator=(const Index&)=delete;
        std::vector<Doc> _forwardIndex;
        std::unordered_map<std::string,std::vector<InvertedDoc>> _invertedIndex;
        static Index* instance;
    public:
        static Index* get(){
            static mutex lock;
            if(instance==nullptr)
            {
                lockGuard guard(lock);
                if(instance==nullptr){
                    instance = new Index();
                }
            }
            return instance;
        }
        Doc* GetDoc(uint64_t id){
            if(id>=_forwardIndex.size()){
                return nullptr;
            }
            return &_forwardIndex[id];
        }
        std::vector<InvertedDoc>* GetInvertedDocs(const std::string& key){
            auto iter = _invertedIndex.find(key);
            if(iter==_invertedIndex.end()){
                return nullptr;
            }
            return &(iter->second);
        }

        bool BuildIndex(const std::string& src)
        {
            std::string line;
            std::ifstream ifs(src,std::ios::binary);
            const std::string sep = "\3";
            if(!ifs.is_open()){
                return false;
            }
            int count = 0;
            while(std::getline(ifs,line))
            {
                std::vector<std::string> elements;
                Util::Split(line,sep,&elements);
                if(elements.size()!=3){
                    continue;
                }
                Doc doc(elements[0],elements[1],elements[2]);
                BuildInvertIndex(BuildForwardIndex(doc));
                ++count;
                if(count %50 ==0){
                    LOG(logLevel::INFO)<<"正在建立索引- " <<count;
                }
            }
            LOG(logLevel::INFO)<<"索引建立成功...";
            return false;
        }
    private:
        Doc& BuildForwardIndex(Doc& doc)
        {
            doc._id = _forwardIndex.size();
            _forwardIndex.push_back(std::move(doc));

            return _forwardIndex.back();
        }
        void BuildInvertIndex(const Doc& doc)
        {
            std::vector<std::string> titles,bodies;
            JiebaUtil::CutString(doc._title,&titles);
            JiebaUtil::CutString(doc._body,&bodies);
            //建立关键词到文档id的映射
            std::unordered_map<std::string,int> weight;
            for(auto& s:titles){
                boost::to_lower(s);
                weight[s]+=5;
            }
            for(auto& s:bodies){
                boost::to_lower(s);
                ++weight[s];
            }

            for(const auto& p:weight){
                InvertedDoc iDoc(doc._id,p.first,p.second);
                _invertedIndex[p.first].push_back(std::move(iDoc));
            }

        }

};

Index* Index::instance = nullptr;